home *** CD-ROM | disk | FTP | other *** search
- /**
- ** sipp - SImple Polygon Processor
- **
- ** A general 3d graphic package
- **
- ** Copyright Equivalent Software HB 1992
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 1, or any later version.
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- ** You can receive a copy of the GNU General Public License from the
- ** Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- **/
-
- /**
- ** objects.c - Functions that handles object and surface creation
- ** and the object hierarchies and the object database.
- **/
-
- #include <stdio.h>
-
- #include <objects.h>
- #include <sipp.h>
- #include <smalloc.h>
-
-
- Object *sipp_world; /* The world that is rendered */
-
- static Vertex *vertex_tree; /* Vertex tree for current object. */
- static Vertex_ref *vertex_stack; /* Vertex stack for current polygon. */
- static int nvertices; /* Number of vertices on the stack */
- static Vertex_ref *vstack_bottom; /* Last entry in vertex stack. */
- static Polygon *poly_stack; /* Polygon stack for current object. */
- static int first_vertex; /* Used when determining if we are */
- /* installing the first vertex in an */
- /* object. *Not* a boolean! */
- static double dist_limit; /* Minimal distance between two */
- /* vertices without them being */
- /* considered to be the same vertex. */
-
-
-
- /*
- * Search for a vertex in a vertex tree. Vertices are asumed
- * to be equal if they differ less than dist_limit in all directions.
- *
- * If the vertex is not found, install it in the tree.
- */
- static Vertex *
- vertex_lookup(pos, texture, p)
- Vector *pos;
- Vector *texture;
- Vertex **p;
- {
- Vector dist;
-
- if (*p == NULL) {
- *p = (Vertex *)smalloc(sizeof(Vertex));
- (*p)->pos = *pos;
- (*p)->texture = *texture;
- MakeVector((*p)->normal, 0.0, 0.0, 0.0);
- (*p)->big = NULL;
- (*p)->sml = NULL;
- return *p;
- } else {
- VecSub(dist, *pos, (*p)->pos);
- if (dist.x > dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->big)));
- } else if (dist.x < -dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->sml)));
- } else if (dist.y > dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->big)));
- } else if (dist.y < -dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->sml)));
- } else if (dist.z > dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->big)));
- } else if (dist.z < -dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->sml)));
- } else {
- VecSub(dist, *texture, (*p)->texture);
- if (dist.x > dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->big)));
- } else if (dist.x < -dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->sml)));
- } else if (dist.y > dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->big)));
- } else if (dist.y < -dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->sml)));
- } else if (dist.z > dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->big)));
- } else if (dist.z < -dist_limit) {
- return (vertex_lookup(pos, texture, &((*p)->sml)));
- } else {
- return *p;
- }
- }
- }
- }
-
-
-
- /*
- * Push a vertex on the vertex stack (without texture coordinates).
- */
- void
- vertex_push(x, y, z)
- double x, y, z;
- {
- vertex_tx_push(x, y, z, (double)0.0, (double)0.0, (double)0.0);
- }
-
-
-
- /*
- * Push a vertex on the vertex stack (with texture coordinates).
- */
- void
- vertex_tx_push(x, y, z, u, v, w)
- double x, y, z, u, v, w;
- {
- Vector pos;
- Vector texture;
- Vertex_ref *vref;
-
- MakeVector(pos, x, y, z);
- MakeVector(texture, u, v, w);
-
- /*
- * To get a reasonable dist_limit we use the following "heuristic"
- * value:
- * The distance between the first two vertices installed in
- * the surface, multiplied by the magic number 1e-10, unless
- * they are the same vertex. In that case 1e-10 is used until
- * we get a vertex that differs from the first.
- */
- if (!first_vertex) {
- first_vertex++;
- } else if (first_vertex == 1) {
- dist_limit = sqrt((x - vertex_tree->pos.x) * (x - vertex_tree->pos.x)
- + (y - vertex_tree->pos.y) * (y - vertex_tree->pos.y)
- + (z - vertex_tree->pos.z) * (z - vertex_tree->pos.z))
- * 1e-10; /* Magic!!! */
- if (dist_limit != 0.0)
- first_vertex++;
- else
- dist_limit = 1e-10; /* More Magic */
- }
- vref = (Vertex_ref *)smalloc(sizeof(Vertex_ref));
- if (vertex_stack == NULL) {
- vertex_stack = vref;
- } else {
- vstack_bottom->next = vref;
- }
- vstack_bottom = vref;
- vref->vertex = vertex_lookup(&pos, &texture, &vertex_tree);
- vref->next = NULL;
- nvertices++;
- }
-
-
-
- /*
- * Push a polygon on the polygon stack. Empty the vertex stack afterwards.
- */
- void
- polygon_push()
- {
- Polygon *polyref;
- Vertex_ref *vref;
- int i;
-
- if (vertex_stack != NULL) {
- polyref = (Polygon *)smalloc(sizeof(Polygon));
- polyref->vertex = (Vertex **)smalloc(nvertices * sizeof(Vertex *));
- for (i = 0; i < nvertices; i++) {
- polyref->vertex[i] = vertex_stack->vertex;
- vref = vertex_stack;
- vertex_stack = vertex_stack->next;
- sfree(vref);
- }
- polyref->nvertices = nvertices;
- nvertices = 0;
- polyref->backface = 0;
- polyref->next = poly_stack;
- poly_stack = polyref;
- }
- }
-
-
-
- /*
- * Create a surface of all polygons in the polygon stack.
- * Empty the polygon stack afterwards.
- */
- Surface *
- surface_create(surf_desc, shader)
- void *surf_desc;
- Shader *shader;
- {
- Surface *surfref;
- Polygon *polyref;
- int i;
-
- if (poly_stack != NULL) {
- surfref = (Surface *)smalloc(sizeof(Surface));
- surfref->vertices = vertex_tree;
- surfref->polygons = poly_stack;
- surfref->surface = surf_desc;
- surfref->shader = shader;
- surfref->ref_count = 0;
- surfref->next = NULL;
- vertex_tree = NULL;
- poly_stack = NULL;
- first_vertex = 0;
- return surfref;
- } else
- return NULL;
- }
-
-
-
- /*
- * Create a surface to be shaded with the simple shader.
- */
- Surface *
- surface_basic_create(ambient, red, grn, blu, specular, c3,
- opred, opgrn, opblu)
- double ambient;
- double red, grn, blu;
- double specular;
- double c3;
- double opred, opgrn, opblu;
- {
- Surf_desc *surf_desc;
-
- surf_desc = (Surf_desc *)smalloc(sizeof(Surf_desc));
- surf_desc->ambient = ambient;
- surf_desc->color.red = red;
- surf_desc->color.grn = grn;
- surf_desc->color.blu = blu;
- surf_desc->specular = specular;
- surf_desc->c3 = c3;
- surf_desc->opacity.red = opred;
- surf_desc->opacity.grn = opgrn;
- surf_desc->opacity.blu = opblu;
-
- return surface_create(surf_desc, basic_shader);
- }
-
-
-
- /*
- * Set SURFACE to be shaded with the shading function SHADER
- * using the surface description SURF_DESC.
- */
- void
- surface_set_shader(surface, surf_desc, shader)
- Surface *surface;
- void *surf_desc;
- Shader *shader;
- {
-
- if (surface != NULL) {
- surface->surface = surf_desc;
- surface->shader = shader;
- }
- }
-
-
-
- /*
- * Set SURFACE to be shaded with the simple shader.
- */
- void
- surface_basic_shader(surface, ambient, red, grn, blu, specular, c3,
- opred, opgrn, opblu)
- Surface *surface;
- double ambient;
- double red, grn, blu;
- double specular;
- double c3;
- double opred, opgrn, opblu;
- {
- Surf_desc *surf_desc;
-
- surf_desc = (Surf_desc *)smalloc(sizeof(Surf_desc));
- surf_desc->ambient = ambient;
- surf_desc->color.red = red;
- surf_desc->color.grn = grn;
- surf_desc->color.blu = blu;
- surf_desc->specular = specular;
- surf_desc->c3 = c3;
- surf_desc->opacity.red = opred;
- surf_desc->opacity.grn = opgrn;
- surf_desc->opacity.blu = opblu;
- surface_set_shader(surface, surf_desc, basic_shader);
- }
-
-
-
- /*
- * Copy a vertex tree.
- */
- static Vertex *
- copy_vertices(vp)
- Vertex *vp;
- {
- Vertex *tmp;
-
- if (vp == NULL)
- return NULL;
- tmp = (Vertex *)smalloc(sizeof(Vertex));
- *tmp = *vp;
- tmp->big = copy_vertices(vp->big);
- tmp->sml = copy_vertices(vp->sml);
- return tmp;
- }
-
-
-
- /*
- * Copy a list of polygons.
- */
- static Polygon *
- copy_polygons(pp, surface)
- Polygon *pp;
- Surface *surface;
- {
- Polygon *tmp;
- int i;
-
- if (pp == NULL)
- return NULL;
- tmp = (Polygon *)smalloc(sizeof(Polygon));
- tmp->nvertices = pp->nvertices;
- tmp->vertex = (Vertex **)smalloc(tmp->nvertices * sizeof(Vertex *));
- for (i = 0; i < tmp->nvertices; i++) {
- tmp->vertex[i] = vertex_lookup(&pp->vertex[i]->pos,
- &pp->vertex[i]->texture,
- &surface->vertices);
- }
- tmp->next = copy_polygons(pp->next, surface);
- return tmp;
- }
-
-
-
- /*
- * Copy a list of surfaces. All polygons and vertices are copied but
- * the shader and surface descriptions are the same as in the
- * original surfaces.
- */
- static Surface *
- surface_copy(surface)
- Surface *surface;
- {
- Surface *newsurf;
-
- if (surface != NULL) {
- newsurf = (Surface *)smalloc(sizeof(Surface));
- if (newsurf == NULL) {
- return NULL;
- }
- memcpy(newsurf, surface, sizeof(Surface));
- newsurf->vertices = copy_vertices(surface->vertices);
- newsurf->polygons = copy_polygons(surface->polygons, newsurf);
- newsurf->ref_count = 1;
- newsurf->next = surface_copy(surface->next);
- return newsurf;
- } else {
- return NULL;
- }
- }
-
-
-
- /*
- * Delete a vertex tree.
- */
- static void
- delete_vertices(vtree)
- Vertex **vtree;
- {
- if (*vtree != NULL) {
- delete_vertices(&((*vtree)->big));
- delete_vertices(&((*vtree)->sml));
- sfree(*vtree);
- *vtree = NULL;
- }
- }
-
-
-
- /*
- * Delete a surface list.
- */
- static void
- surface_delete(surface)
- Surface *surface;
- {
- Polygon *polyref1, *polyref2;
-
- if (surface != NULL) {
- if (--surface->ref_count == 0) {
- if (surface->next != NULL) {
- surface_delete(surface->next);
- }
- polyref2 = surface->polygons;
- while (polyref2 != NULL) {
- sfree(polyref2->vertex);
- polyref1 = polyref2;
- polyref2 = polyref2->next;
- sfree(polyref1);
- }
- delete_vertices(&(surface->vertices));
- sfree(surface);
- }
- }
- }
-
-
-
- /*
- * Create an empty object.
- */
- Object *
- object_create()
- {
- Object *obj;
-
- obj = (Object *)smalloc(sizeof(Object));
- obj->surfaces = NULL;
- obj->sub_obj = NULL;
- MatCopy(&obj->transf, &ident_matrix);
- obj->ref_count = 0;
- obj->next = NULL;
-
- return obj;
- }
-
-
-
- /*
- * Copy the top object in an object hierarchy.
- * The new object will reference the same
- * subobjects and surfaces as the original object.
- * if REF_COUNT_UPDATE is true, the reference counts
- * in the surfaces and subobjects will be incremented.
- */
- static Object *
- object_copy(object, ref_count_update)
- Object *object;
- bool ref_count_update;
- {
- Object *newobj;
-
- if (object == NULL) {
- return NULL;
- }
-
- if ((newobj = (Object *)smalloc(sizeof(Object))) != NULL) {
- memcpy(newobj, object, sizeof(Object));
- if (ref_count_update) {
- if (newobj->sub_obj != NULL) {
- newobj->sub_obj->ref_count++;
- }
- if (newobj->surfaces != NULL) {
- newobj->surfaces->ref_count++;
- }
- }
- MatCopy(&newobj->transf, &ident_matrix);
- newobj->ref_count = 0;
- newobj->next = NULL;
- }
-
- return newobj;
- }
-
-
-
- /*
- * Copy a list of objects. If SURF_COPY is true
- * the surfaces in the objects will be copied too.
- */
- static Object *
- object_list_copy(object, surf_copy)
- Object *object;
- bool surf_copy;
- {
- Object *newobj;
-
- if (object == NULL) {
- return NULL;
- }
-
- if ((newobj = (Object *)smalloc(sizeof(Object))) != NULL) {
- memcpy(newobj, object, sizeof(Object));
- newobj->ref_count = 0;
- } else {
- return NULL;
- }
-
- if (surf_copy) {
- newobj->surfaces = surface_copy(object->surfaces);
- } else if (newobj->surfaces != NULL){
- newobj->surfaces->ref_count++;
- }
-
- newobj->sub_obj = object_list_copy(object->sub_obj, surf_copy);
- if (newobj->sub_obj != NULL) {
- newobj->sub_obj->ref_count++;
- }
- newobj->next = object_list_copy(object->next, surf_copy);
- if (newobj->next != NULL) {
- newobj->next->ref_count++;
- }
-
- return newobj;
- }
-
-
-
- /*
- * Copy the top node of an object hierarchy. The
- * subobjects and surface references will be the
- * same as in the original.
- */
- Object *
- object_instance(obj)
- Object *obj;
- {
- return object_copy(obj, TRUE);
- }
-
-
-
- /*
- * Copy an object hierarchy. The objects in
- * the new hierarchy will reference the same
- * surfaces as the object in
- * the old hierarchy, but all object nodes
- * will be duplicated.
- */
- Object *
- object_dup(object)
- Object *object;
- {
- Object *newobj;
-
- if ((newobj = object_copy(object, FALSE)) == NULL) {
- return NULL;
- }
-
- newobj->sub_obj = object_list_copy(object->sub_obj, FALSE);
- newobj->next = NULL;
-
- return newobj;
- }
-
-
-
- /*
- * Copy an object hierarchy. All object nodes
- * and surfaces in the old hierarchy
- * will be duplicated.
- */
- Object *
- object_deep_dup(object)
- Object *object;
- {
- Object *newobj;
-
- if ((newobj = object_copy(object, FALSE)) == NULL) {
- return NULL;
- }
-
- newobj->surfaces = surface_copy(object->surfaces);
- newobj->sub_obj = object_list_copy(object->sub_obj, TRUE);
- newobj->next = NULL;
-
- return newobj;
- }
-
-
-
- /*
- * Recursively delete an object hierarchy. Reference
- * counts are decremented and if the result is zero
- * the recursion continues and the memory used is freed.
- */
- static void
- r_object_delete(object)
- Object * object;
- {
- if (object != NULL) {
- if (--object->ref_count == 0) {
- surface_delete(object->surfaces);
- r_object_delete(object->sub_obj);
- r_object_delete(object->next);
- sfree(object);
- }
- }
- }
-
-
-
- /*
- * Delete an object hierarchy. This is only possible to do on
- * a top level object in order to avoid dangeling references.
- * Don't allow deletion of the world either...
- */
- void
- object_delete(object)
- Object * object;
- {
- if (object != NULL && object != sipp_world) {
- if (object->ref_count == 0) { /* Is it a top level object? */
- surface_delete(object->surfaces);
- r_object_delete(object->sub_obj);
- r_object_delete(object->next);
- sfree(object);
- }
- }
- }
-
-
-
- /*
- * Remove SUBOBJ as a subobject in OBJECT. SUBOBJ is only
- * removed from the list of subojects in OBJECT. If the
- * memory it uses should be freed, object_delete() must
- * be used.
- */
- void
- object_sub_subobj(object, subobj)
- Object *object, *subobj;
- {
- Object *oref1;
- Object *oref2;
-
- if (object == NULL || subobj == NULL || object->sub_obj == NULL) {
- return;
- }
-
- if (object->sub_obj == subobj) {
- object->sub_obj = subobj->next;
- } else {
- oref1 = object->sub_obj;
- oref2 = oref1->next;
- while (oref2 != NULL && oref2 != subobj) {
- oref1 = oref2;
- oref2 = oref2->next;
- }
- if (oref2 == subobj) {
- oref1->next = oref2->next;
- }
- }
-
- subobj->ref_count--;
- }
-
-
-
- /*
- * Add SUBOBJ as a subobject in OBJECT. SUBOBJ is appended
- * on the *end* of OBJECT's subobject list,
- * so that if SUBOBJ, for some obscure reason,
- * were the head of an object list, we don't loose
- * the rest of that list.
- * Remove SUBOBJ from the rendering database since it is no
- * longer a root object in an hierarchy.
- */
- void
- object_add_subobj(object, subobj)
- Object *object, *subobj;
- {
- Object *oref;
-
- if (object == NULL || subobj == NULL) {
- return;
- }
-
- if (object->sub_obj == NULL) {
- object->sub_obj = subobj;
- } else {
- oref = object->sub_obj;
- while (oref->next != NULL) {
- oref = oref->next;
- }
- oref->next = subobj;
- }
-
- subobj->ref_count++;
- }
-
-
-
- /*
- * Remove SURFACE as a surface in OBJECT.
- */
- void
- object_sub_surface(object, surface)
- Object *object;
- Surface *surface;
- {
- Surface *sref1;
- Surface *sref2;
-
- if (object == NULL || surface == NULL || object->surfaces == NULL) {
- return;
- }
-
- if (object->surfaces == surface) {
- object->surfaces = surface->next;
- } else {
- sref1 = object->surfaces;
- sref2 = sref1->next;
- while (sref2 != NULL && sref2 != surface) {
- sref1 = sref2;
- sref2 = sref2->next;
- }
- if (sref2 == surface) {
- sref1->next = sref2->next;
- }
- }
-
- surface->ref_count--;
- }
-
-
-
- /*
- * Add SURFACE to the list of surfaces belonging
- * to OBJECT. SURFACE is appended on the *end* of the
- * list for the same reasons as in object_add_subobj.
- */
- void
- object_add_surface(object, surface)
- Object *object;
- Surface *surface;
- {
- Surface *sref;
-
- if (object == NULL || surface == NULL) {
- return;
- }
-
- if (object->surfaces == NULL) {
- object->surfaces = surface;
- } else {
- sref = object->surfaces;
- while (sref->next != NULL) {
- sref = sref->next;
- }
- sref->next = surface;
- }
-
- surface->ref_count++;
- }
-
-
-
- /*
- * Initialize the data structures.
- */
- void
- objects_init()
- {
- vertex_tree = NULL;
- vertex_stack = NULL;
- nvertices = 0;
- first_vertex = 0;
- poly_stack = NULL;
- sipp_world = object_create();
- }
-